﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Xml;
using System.Xml.Schema;

namespace ObjectSearch.Configuration
{
    public class Configuration
    {
        /// <summary>
        /// A private field used to hold the value for RegisteredObjects property.
        /// </summary>
        private List<ObjectMetaData> _RegisteredObjects;

        public List<ObjectMetaData> RegisteredObjects
        {
            get
            {
                return _RegisteredObjects;
            }
        }

        public Configuration()
        {
            _RegisteredObjects = new List<ObjectMetaData>();
        }

        public void AddClass(Type t)
        {
            this.LookForAttributes(t);
        }

        public void AddClass<T>()
        {
            this.AddClass(typeof(T));
        }

        public void AddAssembly(Assembly assembly)
        {
            string[] resourceNames = assembly.GetManifestResourceNames();

            foreach (string resourceName in resourceNames)
            {
                if (resourceName.EndsWith("idx.xml"))
                {
                    using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName))
                    {
                        using (XmlReader reader = XmlReader.Create(resourceStream))
                        {
                            ReadFromXml(reader);
                        }
                    }                    
                }
            }
        }

        private void LookForAttributes(Type t)
        {
            object[] customAtributes =  t.GetCustomAttributes(typeof(DocumentAttribute), true);

            foreach (object item in customAtributes)
            {
                if (item is DocumentAttribute)
                {
                    DocumentAttribute docAttribute = item as DocumentAttribute;

                    ObjectMetaData objectMeta = new ObjectMetaData();
                    objectMeta.ObjectType = t;

                    PropertyInfo[] allProperties = t.GetProperties();

                    foreach (PropertyInfo propertyInfo in allProperties)
                    {
                        object[] fieldAttributes = propertyInfo.GetCustomAttributes(typeof(FieldAttribute), true);

                        if (fieldAttributes.Length > 0)
                        {
                            FieldMetaData f = new FieldMetaData();
                            FieldAttribute a = fieldAttributes[0] as FieldAttribute;
                            
                            f.MemberName = propertyInfo.Name;
                            f.FieldName = a.Name;
                            f.Boost = a.Boost;
                            f.IndexOptions = a.IndexOptions;
                            f.IsDefaultSearchField = a.IsDefaultSearchField;
                            f.SortingOptions = a.SortingOptions;
                            f.StorageOptions = a.StorageOptions;
                            f.TermVectorOptions = a.TermVectorOptions;
                            f.IsUniqueKey = a.IsUniqueKey;

                            objectMeta.Fields.Add(f);                           
                        }                        
                    }

                    if (objectMeta.Fields.Count > 0)
                    {
                        this.RegisteredObjects.Add(objectMeta);
                        return;
                    }
                }
            }
        }
        private void ReadFromXml(XmlReader reader)
        {
            XmlDocument document = new XmlDocument();
            document.Load(reader);

            var root = document.SelectSingleNode("SearchMapping");

            foreach (XmlNode childNode in root.ChildNodes)
            {
                if (childNode.Name.ToLower() == "document")
                {
                    ObjectMetaData objMeta = new ObjectMetaData();
                    objMeta.ObjectType = Type.GetType(childNode.Attributes["class"].Value);


                    foreach (XmlNode fieldNode in childNode.ChildNodes)
                    {
                        if (fieldNode.Name.ToLower() == "field")
                        {
                            FieldMetaData f = new FieldMetaData();
                            
                            foreach (XmlAttribute attribute in fieldNode.Attributes)
                            {
                                switch (attribute.Name.ToLower())
                                {
                                    case "sortingoptions":
                                        f.SortingOptions = (SortOptions)Enum.Parse(typeof(SortOptions), attribute.Value, true);
                                        break;
                                    case "isuniquekey":
                                        f.IsUniqueKey = bool.Parse(attribute.Value);
                                        break;
                                    case "name":
                                        f.FieldName = attribute.Value;
                                        break;
                                    case "membername":
                                        f.MemberName = attribute.Value;
                                        break;
                                    case "boost":
                                        f.Boost = (float.Parse(attribute.Value));
                                        break;
                                    case "isdefaultsearchfield":
                                        f.IsDefaultSearchField = bool.Parse(attribute.Value);
                                        break;
                                    case "indexoptions":
                                        f.IndexOptions = (IndexOptions)Enum.Parse(typeof(IndexOptions), attribute.Value, true);
                                        break;
                                    case "storageoptions":
                                        f.StorageOptions = (StorageOptions)Enum.Parse(typeof(StorageOptions), attribute.Value, true);
                                        break;
                                    case "termvectoroptions":
                                        f.TermVectorOptions = (TermVectorOptions)Enum.Parse(typeof(TermVectorOptions), attribute.Value, true);
                                        break;
                                }

                            }

                            objMeta.Fields.Add(f);
                        }
                    }

                    this.RegisteredObjects.Add(objMeta);
                }
            }



            var nodes = document.SelectNodes("SearchMappings/document");
        }


        public ObjectMetaData FindMetaData(Type objectType)
        {
            foreach (var item in RegisteredObjects)
            {
                if (item.ObjectType == objectType)
                {
                    return item;
                }
            }

            return null;
        }
        public ObjectMetaData FindMetaData<T>()
        {
            return this.FindMetaData(typeof(T));
        }
    }
}
